﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Castle.Core.Interceptor;
using EAV.Data;
using System.Data;
using System.Reflection;

namespace EAV.Bridge
{
    public class ItemInterceptor : IInterceptor
    {
        Entity _entity;
        TypeInfo _info;
        EAVContext _context;

        public ItemInterceptor(Entity e, TypeInfo info, EAVContext context)
        {
            _entity = e;
            _info = info;
            _context = context;
        }

        #region IInterceptor Members

        public void Intercept(IInvocation invocation)
        {
            if (IsValueSet(invocation.Method.Name))
            {
                WriteThrough(invocation);
                invocation.Proceed();
            }
            else if (IsValueGet(invocation.Method.Name))
            {
                invocation.ReturnValue = ReadThrough(invocation);
            }
            else if (invocation.Method.Name == "Save")
            {
                _context.SaveChanges();

                foreach (string relation in _info.Relations.Keys)
                {
                    PropertyInfo relationProperty = invocation.Proxy.GetType().GetProperty(relation);
                    object relationValue = relationProperty.GetValue(invocation.Proxy, null);

                    foreach (BaseItem item in ((System.Collections.IEnumerable)relationValue))
                    {
                        item.Save();
                    }
                }
            }
            else if (invocation.Method.Name == "SetForeignKey")
            {
                WriteThrough(invocation.Arguments[0] as string, invocation.Arguments[1]);
            }
            else
            {
                invocation.Proceed();
            }
        }        

        #endregion

        private void WriteThrough(IInvocation invocation)
        {
            string propertyName = invocation.Method.Name.Replace("set_", string.Empty);
            string valueName = _info.MapPropertiesOn[propertyName];

            WriteThrough(valueName, invocation.Arguments[0]);   
        }
        private void WriteThrough(string valueName, object propertyValue)
        {
            if (!_entity.Value.IsLoaded &&
                _entity.EntityState != EntityState.Detached &&
                _entity.EntityState != EntityState.Added)
            {
                _entity.Value.Load();
            }

            Value value = _entity.Value.FirstOrDefault(v => v.Name == valueName);

            if (value == null)
            {
                value = new Value();
                value.ID = Guid.NewGuid();
                value.Name = valueName;

                _entity.Value.Add(value);
            }

            SetValue(value, propertyValue);
        }

        private void SetValue(Value value, object arg)
        {
            if (arg is int?)
                value.IntValue = arg as int?;
            else if (arg is Guid?)
                value.GuidValue = arg as Guid?;
            else if (arg is double?)
                value.NumericValue = arg as double?;
            else if (arg is DateTime?)
                value.DateTimeValue = arg as DateTime?;
            else if (arg is string)
                value.StringValue = arg as string;
        }

        private bool IsValueSet(string methodName)
        {
            if (methodName.StartsWith("set_"))
            {
                string propertyName = methodName.Replace("set_", string.Empty);
                return _info.MapPropertiesOn.ContainsKey(propertyName);
            }

            return false;
        }

        private bool IsValueGet(string methodName)
        {
            if (methodName.StartsWith("get_"))
            {
                string propertyName = methodName.Replace("get_", string.Empty);
                return _info.MapPropertiesOn.ContainsKey(propertyName);
            }

            return false;
        }

        private object ReadThrough(IInvocation invocation)
        {
            string propertyName = invocation.Method.Name.Replace("get_", string.Empty);
            string valueName = _info.MapPropertiesOn[propertyName];

            //throw new NotImplementedException();
            if (!_entity.Value.IsLoaded &&
                _entity.EntityState != EntityState.Detached &&
                _entity.EntityState != EntityState.Added)
            {
                _entity.Value.Load();
            }

            Value value = _entity.Value.FirstOrDefault(v => v.Name == valueName);

            if (value != null)
            {
                return GetValue(value, invocation.Method.ReturnType);
            }

            return null;
        }

        private object GetValue(Value value, Type type)
        {
            if (type == typeof(int?))
                return value.IntValue;
            else if (type == typeof(Guid?))
                return value.GuidValue;
            else if (type == typeof(DateTime?))
                return value.DateTimeValue;
            else if (type == typeof(double?))
                return value.NumericValue;
            else if (type == typeof(string))
                return value.StringValue;
            else
                return null;
        }
    }
}
